//
//  NSNotificationCenter+YYAdd.m
//  YYKit <https://github.com/ibireme/YYKit>
//
//  Created by ibireme on 13/8/24.
//  Copyright (c) 2015 ibireme.
//
//  This source code is licensed under the MIT-style license found in the
//  LICENSE file in the root directory of this source tree.
//

#import "NSNotificationCenter+YYAdd.h"
#import "NSObject+YYAdd.h"
#include <pthread.h>
#import "YYKitMacro.h"

YYSYNTH_DUMMY_CLASS(NSNotificationCenter_YYAdd)

static const void *observer_set_key;

@implementation NSNotificationCenter (YYAdd)

- (void)postNotificationOnMainThread:(NSNotification *)notification {
    if (pthread_main_np()) return [self postNotification:notification];
    [self postNotificationOnMainThread:notification waitUntilDone:NO];
}

- (void)postNotificationOnMainThread:(NSNotification *)notification waitUntilDone:(BOOL)wait {
    if (pthread_main_np()) return [self postNotification:notification];
    [[self class] performSelectorOnMainThread:@selector(_yy_postNotification:) withObject:notification waitUntilDone:wait];
}

- (void)postNotificationOnMainThreadWithName:(NSString *)name object:(id)object {
    if (pthread_main_np()) return [self postNotificationName:name object:object userInfo:nil];
    [self postNotificationOnMainThreadWithName:name object:object userInfo:nil waitUntilDone:NO];
}

- (void)postNotificationOnMainThreadWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo {
    if (pthread_main_np()) return [self postNotificationName:name object:object userInfo:userInfo];
    [self postNotificationOnMainThreadWithName:name object:object userInfo:userInfo waitUntilDone:NO];
}

- (void)postNotificationOnMainThreadWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo waitUntilDone:(BOOL)wait {
    if (pthread_main_np()) return [self postNotificationName:name object:object userInfo:userInfo];
    NSMutableDictionary *info = [[NSMutableDictionary allocWithZone:nil] initWithCapacity:3];
    if (name) [info setObject:name forKey:@"name"];
    if (object) [info setObject:object forKey:@"object"];
    if (userInfo) [info setObject:userInfo forKey:@"userInfo"];
    [[self class] performSelectorOnMainThread:@selector(_yy_postNotificationName:) withObject:info waitUntilDone:wait];
}

+ (void)_yy_postNotification:(NSNotification *)notification {
    [[self defaultCenter] postNotification:notification];
}

+ (void)_yy_postNotificationName:(NSDictionary *)info {
    NSString *name = [info objectForKey:@"name"];
    id object = [info objectForKey:@"object"];
    NSDictionary *userInfo = [info objectForKey:@"userInfo"];
    
    [[self defaultCenter] postNotificationName:name object:object userInfo:userInfo];
}

- (void)addObserverForName:(NSNotificationName)aName binder:(id)binder object:(id)anObject usingBlock:(void (^)(NSNotification * _Nonnull))block {
    NSMutableSet *observerSet = [binder getAssociatedValueForKey:&observer_set_key];
    if (observerSet == nil) {
        observerSet = [NSMutableSet set];
        [binder setAssociateValue:observerSet withKey:&observer_set_key];
    }
    id<NSObject> observer = [self addObserverForName:aName object:anObject queue:[NSOperationQueue mainQueue] usingBlock:block];
    [observerSet addObject:@{@"observer":observer,@"name":aName}];
}

+ (void)initialize {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleInstanceMethod:@selector(removeObserver:name:object:) with:@selector(yy_removeObserver:name:object:)];
        [self swizzleInstanceMethod:@selector(removeObserver:) with:@selector(yy_removeObserver:)];
    });
}

- (void)yy_removeObserver:(id)observer name:(NSNotificationName)aName object:(id)anObject {
    ///移除target-action的观察者
    [self yy_removeObserver:observer name:aName object:anObject];
    ///移除block观察者
    NSMutableSet *observerSet = [observer getAssociatedValueForKey:&observer_set_key];
    [observerSet enumerateObjectsUsingBlock:^(id  _Nonnull obj, BOOL * _Nonnull stop) {
        if ([[obj objectForKey:@"name"] isEqualToString:aName]) {
            [self yy_removeObserver:[obj objectForKey:@"observer"] name:aName object:anObject];
            [observerSet removeObject:obj];
        }
    }];
    
}

- (void)yy_removeObserver:(id)observer {
    ///移除target-action的观察者
    [self yy_removeObserver:observer];
    ///移除block观察者
    NSMutableSet *observerSet = [observer getAssociatedValueForKey:&observer_set_key];
    [observerSet enumerateObjectsUsingBlock:^(id  _Nonnull obj, BOOL * _Nonnull stop) {
        [self yy_removeObserver:[obj objectForKey:@"observer"]];
    }];
    ///移除ObserverSet
    [observer setAssociateValue:nil withKey:&observer_set_key];
}

@end
